package com.access.boot.autoconfigure.apisdk.aop;

import org.springframework.aop.support.AopUtils;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;
import java.util.List;

/**
 * @author Yangzhen
 * @date 2022/6/2 16:15
 * @since 1.0.0
 */
public class ReflectiveMethodInvocation implements MethodInvocation {

    private List<Object> methodInterceptors;

    protected final Object proxy;

    @Nullable
    protected final Object target;

    protected final Method method;

    // 方法参数
    protected Object[] arguments;

    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, Object[] arguments, List<Object> methodInterceptors) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.methodInterceptors = methodInterceptors;
    }

    @Override
    public Object proceed() throws Throwable {
        if (this.currentInterceptorIndex == this.methodInterceptors.size() - 1) {
            return invokeJoinpoint();
        }
        MethodInterceptor methodInterceptor = (MethodInterceptor) methodInterceptors.get(++currentInterceptorIndex);

        return methodInterceptor.invoke(this);
    }

    @Nullable
    protected Object invokeJoinpoint() throws Throwable {
        return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public Method getMethod() {
        return this.method;
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    // 调整方法请求参数
    public void appendArgument(Object obj) {
        if (this.arguments != null && this.arguments.length > 0) {
            int length = this.arguments.length;
            Object[] newArgs = new Object[length + 1];
            System.arraycopy(this.arguments, 0, newArgs, 0, length);
            newArgs[length] = obj;
            this.arguments = newArgs;
        } else {
            this.arguments = new Object[1];
            this.arguments[0] = obj;
        }
    }
}
